package org.juiceshop.gm.midi;

import java.io.OutputStream;
import java.io.PrintStream;

import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.Sequence;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.SysexMessage;
import javax.sound.midi.Track;

import org.juiceshop.gutil.DynamicObject;

public class SequenceDumper {

	private PrintStream _out;
	
	public SequenceDumper() {
		_out = System.out;
	}
	
	public SequenceDumper(OutputStream out) {
		if (out instanceof PrintStream) {
			_out = (PrintStream)out;
		} else {
			_out = new PrintStream(out);
		}
	}
	
	protected void print(Object...values) {
		for (Object val : values) {
			_out.print(val);
			_out.print(' ');
		}
	}
	
	protected void println(Object...values) {
		print(values);
		_out.println();
	}
	
	
	public void dump (Sequence sequence) {				
		println(show(sequence,"divisionType","resolution","tickLength","microsecondLength","patchList"));
		Track[] tracks = sequence.getTracks();
		for (int trackNo=0; trackNo<tracks.length; trackNo++) {
			Track track = tracks[trackNo];
			println("**** TRACK #"+trackNo+": size:"+track.size()+" ticks:"+track.ticks()+" ***** ");
			for (int i=0; i<track.size(); i++) {
				MidiEvent event = track.get(i);
				MidiMessage msg = event.getMessage();
				long tick = event.getTick();
				print(""+tick+": ");
				if (msg instanceof ShortMessage) {
					println(show(msg,"command","channel","data1","data2"));
				} else if (msg instanceof MetaMessage) {
					println(show(msg,"type","data"));
				} else if (msg instanceof SysexMessage) {
					println(show(msg,"data"));
				} else {
					throw new IllegalStateException("Unknown MIDI message type: "+msg.getClass());
				}
			}
		}
	}
	
	public void dump (MidiSequence sequence) {
		println(show(sequence,"resolution"));
		for (MidiSequence.Event ev : sequence) {
			println(ev);
		}
	}
	
	protected static String show (Object obj, String...fields) {
		DynamicObject<?> dynobj = DynamicObject.create(obj);
		StringBuilder sb = new StringBuilder();
		sb.append(obj.getClass().getSimpleName()+": ");
		boolean first = true;
		for (String field : fields) {
			if (first) {
				first = false;
			} else {
				sb.append(", ");
			}
			sb.append(field).append('=');
			Object value = dynobj.get(field);
			if (value instanceof byte[]) {
				sb.append(join((byte[])value));
			} else {
				sb.append(value);
			}
		}
		return sb.toString();
	}
	
	private static String join(byte[] array) {
		StringBuilder sb = new StringBuilder();
		sb.append('[');
		boolean first1 = true;
		for (byte b : array) {
			if (first1) {
				first1 = false;
			} else {
				sb.append(',');
			}
			sb.append(b);
		}
		return sb.append(']').toString();
	}
	
}
